/*
 * MIT License
 *
 * Copyright (c) 2023-present, tangli
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

package tony.test.crypto.symmetric

import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Nested
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows
import org.junit.jupiter.api.Assertions.assertArrayEquals
import org.junit.jupiter.api.Assertions.assertNotNull
import org.junit.jupiter.api.Assertions.assertTrue
import tony.crypto.symmetric.Des
import javax.crypto.Cipher
import java.security.SecureRandom

/**
 * DES 对称加密算法测试类
 *
 * 测试 DES 算法的具体实现，包括密钥验证、加密解密等功能
 *
 * @author tangli
 * @date 2025/01/22 20:00
 */
@DisplayName("DES 对称加密测试")
class DesTest {

    /**
     * 生成有效的 DES 密钥（8 字节）
     */
    private fun generateValidDesKey(): ByteArray {
        val key = ByteArray(8)
        SecureRandom().nextBytes(key)
        return key
    }

    /**
     * 生成固定的测试密钥
     */
    private fun generateFixedDesKey(): ByteArray {
        return "12345678".toByteArray(Charsets.UTF_8)
    }

    @Nested
    @DisplayName("DES 密钥验证测试")
    inner class DesKeyValidationTest {

        @Test
        @DisplayName("crypto(): 8字节密钥应该正常工作")
        fun testValidKeyLength() {
            // Given
            val validKey = generateValidDesKey()
            val testData = "DES 测试数据".toByteArray(Charsets.UTF_8)

            // When
            val encrypted = Des.crypto(testData, validKey, Cipher.ENCRYPT_MODE)
            val decrypted = Des.crypto(encrypted, validKey, Cipher.DECRYPT_MODE)

            // Then
            assertNotNull(encrypted, "加密结果不应为空")
            assertTrue(encrypted.isNotEmpty(), "加密结果不应为空数组")
            assertArrayEquals(testData, decrypted, "8字节密钥加密解密应该正确")
        }

        @Test
        @DisplayName("crypto(): 16字节密钥应该正常工作（DES只使用前8字节）")
        fun testLongerKeyLength() {
            // Given
            val longerKey = "1234567890123456".toByteArray(Charsets.UTF_8)
            val testData = "DES 长密钥测试".toByteArray(Charsets.UTF_8)

            // When
            val encrypted = Des.crypto(testData, longerKey, Cipher.ENCRYPT_MODE)
            val decrypted = Des.crypto(encrypted, longerKey, Cipher.DECRYPT_MODE)

            // Then
            assertArrayEquals(testData, decrypted, "长密钥加密解密应该正确")
        }

        @Test
        @DisplayName("crypto(): 短密钥应该抛出异常")
        fun testShortKey() {
            // Given
            val shortKey = "1234567".toByteArray(Charsets.UTF_8) // 7 字节
            val testData = "test".toByteArray()

            // When & Then
            assertThrows<Exception> {
                Des.crypto(testData, shortKey, Cipher.ENCRYPT_MODE)
            }
        }

        @Test
        @DisplayName("crypto(): 空密钥应该抛出异常")
        fun testEmptyKey() {
            // Given
            val emptyKey = ByteArray(0)
            val testData = "test".toByteArray()

            // When & Then
            assertThrows<Exception> {
                Des.crypto(testData, emptyKey, Cipher.ENCRYPT_MODE)
            }
        }
    }

    @Nested
    @DisplayName("DES 加密解密测试")
    inner class DesEncryptDecryptTest {

        private val validKey = generateFixedDesKey()

        @Test
        @DisplayName("encrypt() 和 decrypt(): 应该正确处理英文文本")
        fun testEncryptDecryptEnglishText() {
            // Given
            val englishText = "Hello, World! This is a DES test.".toByteArray(Charsets.UTF_8)

            // When
            val encrypted = Des.encrypt(englishText, validKey)
            val decrypted = Des.decrypt(encrypted, validKey)

            // Then
            assertArrayEquals(englishText, decrypted, "英文文本加密解密应该正确")
        }

        @Test
        @DisplayName("encrypt() 和 decrypt(): 应该正确处理中文文本")
        fun testEncryptDecryptChineseText() {
            // Given
            val chineseText = "你好，世界！这是DES测试。".toByteArray(Charsets.UTF_8)

            // When
            val encrypted = Des.encrypt(chineseText, validKey)
            val decrypted = Des.decrypt(encrypted, validKey)

            // Then
            assertArrayEquals(chineseText, decrypted, "中文文本加密解密应该正确")
        }

        @Test
        @DisplayName("encrypt() 和 decrypt(): 应该正确处理二进制数据")
        fun testEncryptDecryptBinaryData() {
            // Given
            val binaryData = byteArrayOf(
                0x00, 0x01, 0x02, 0x03,
                0xFF.toByte(), 0xFE.toByte(), 0xFD.toByte(), 0xFC.toByte()
            )

            // When
            val encrypted = Des.encrypt(binaryData, validKey)
            val decrypted = Des.decrypt(encrypted, validKey)

            // Then
            assertArrayEquals(binaryData, decrypted, "二进制数据加密解密应该正确")
        }

        @Test
        @DisplayName("encrypt() 和 decrypt(): 应该正确处理空数据")
        fun testEncryptDecryptEmptyData() {
            // Given
            val emptyData = ByteArray(0)

            // When
            val encrypted = Des.encrypt(emptyData, validKey)
            val decrypted = Des.decrypt(encrypted, validKey)

            // Then
            assertArrayEquals(emptyData, decrypted, "空数据加密解密应该正确")
        }

        @Test
        @DisplayName("encrypt() 和 decrypt(): 应该正确处理较大数据块")
        fun testEncryptDecryptLargeData() {
            // Given
            val largeData = "DES".repeat(1000).toByteArray(Charsets.UTF_8)

            // When
            val encrypted = Des.encrypt(largeData, validKey)
            val decrypted = Des.decrypt(encrypted, validKey)

            // Then
            assertArrayEquals(largeData, decrypted, "大数据块加密解密应该正确")
        }

        @Test
        @DisplayName("encrypt(): 相同输入应该产生相同密文")
        fun testEncryptDeterministic() {
            // Given
            val testData = "DES确定性测试".toByteArray(Charsets.UTF_8)

            // When
            val encrypted1 = Des.encrypt(testData, validKey)
            val encrypted2 = Des.encrypt(testData, validKey)

            // Then
            assertArrayEquals(encrypted1, encrypted2, "相同输入应该产生相同密文")
        }
    }

    @Nested
    @DisplayName("DES crypto() 方法测试")
    inner class DesCryptoMethodTest {

        private val validKey = generateFixedDesKey()

        @Test
        @DisplayName("crypto(): 加密模式应该正确工作")
        fun testCryptoEncryptMode() {
            // Given
            val testData = "DES crypto 方法测试".toByteArray(Charsets.UTF_8)

            // When
            val encrypted = Des.crypto(testData, validKey, Cipher.ENCRYPT_MODE)
            val decrypted = Des.crypto(encrypted, validKey, Cipher.DECRYPT_MODE)

            // Then
            assertNotNull(encrypted, "加密结果不应为空")
            assertTrue(encrypted.isNotEmpty(), "加密结果不应为空数组")
            assertArrayEquals(testData, decrypted, "crypto 方法加密解密应该正确")
        }

        @Test
        @DisplayName("crypto(): 解密模式应该正确工作")
        fun testCryptoDecryptMode() {
            // Given
            val testData = "DES crypto 解密测试".toByteArray(Charsets.UTF_8)
            val encrypted = Des.encrypt(testData, validKey)

            // When
            val decrypted = Des.crypto(encrypted, validKey, Cipher.DECRYPT_MODE)

            // Then
            assertArrayEquals(testData, decrypted, "crypto 方法解密应该正确")
        }

        @Test
        @DisplayName("crypto(): 无效模式应该抛出异常")
        fun testCryptoInvalidMode() {
            // Given
            val testData = "test".toByteArray()
            val invalidMode = 999

            // When & Then
            assertThrows<Exception> {
                Des.crypto(testData, validKey, invalidMode)
            }
        }
    }

    @Nested
    @DisplayName("DES 错误处理测试")
    inner class DesErrorHandlingTest {

        private val validKey = generateValidDesKey()

        @Test
        @DisplayName("decrypt(): 无效密文应该抛出异常")
        fun testDecryptInvalidCiphertext() {
            // Given
            val invalidCiphertext = "这不是有效的DES密文".toByteArray()

            // When & Then
            assertThrows<Exception> {
                Des.decrypt(invalidCiphertext, validKey)
            }
        }

        @Test
        @DisplayName("decrypt(): 截断的密文应该抛出异常")
        fun testDecryptTruncatedCiphertext() {
            // Given
            val testData = "DES test data".toByteArray()
            val encrypted = Des.encrypt(testData, validKey)
            val truncatedCiphertext = encrypted.copyOf(encrypted.size / 2)

            // When & Then
            assertThrows<Exception> {
                Des.decrypt(truncatedCiphertext, validKey)
            }
        }

        @Test
        @DisplayName("decrypt(): 错误的密钥应该抛出异常")
        fun testDecryptWithWrongKey() {
            // Given
            val testData = "DES test data".toByteArray()
            val correctKey = generateValidDesKey()
            val wrongKey = generateValidDesKey()
            val encrypted = Des.encrypt(testData, correctKey)

            // When & Then
            // DES 使用错误密钥解密会抛出异常（由于填充错误）
            assertThrows<Exception> {
                Des.decrypt(encrypted, wrongKey)
            }
        }
    }

    @Nested
    @DisplayName("DES 性能和稳定性测试")
    inner class DesPerformanceTest {

        @Test
        @DisplayName("encrypt() 和 decrypt(): 多次操作应该保持一致性")
        fun testMultipleOperationsConsistency() {
            // Given
            val key = generateValidDesKey()
            val testData = "DES 一致性测试数据".toByteArray(Charsets.UTF_8)

            // When & Then
            repeat(20) {
                val encrypted = Des.encrypt(testData, key)
                val decrypted = Des.decrypt(encrypted, key)
                assertArrayEquals(testData, decrypted, "第 ${it + 1} 次 DES 加密解密应该一致")
            }
        }

        @Test
        @DisplayName("crypto(): 不同数据大小应该正确处理")
        fun testDifferentDataSizes() {
            // Given
            val key = generateValidDesKey()
            val sizes = listOf(1, 7, 8, 9, 15, 16, 17, 31, 32, 33)

            // When & Then
            sizes.forEach { size ->
                val testData = "D".repeat(size).toByteArray(Charsets.UTF_8)
                val encrypted = Des.crypto(testData, key, Cipher.ENCRYPT_MODE)
                val decrypted = Des.crypto(encrypted, key, Cipher.DECRYPT_MODE)
                assertArrayEquals(testData, decrypted, "大小为 $size 的数据应该正确处理")
            }
        }

        @Test
        @DisplayName("crypto(): 边界数据应该正确处理")
        fun testBoundaryData() {
            // Given
            val key = generateValidDesKey()
            val boundaryData = listOf(
                ByteArray(0), // 空数据
                byteArrayOf(0x00), // 单字节零
                byteArrayOf(0xFF.toByte()), // 单字节最大值
                ByteArray(8) { 0x00 }, // 8字节零（DES块大小）
                ByteArray(8) { 0xFF.toByte() } // 8 字节最大值
            )

            // When & Then
            boundaryData.forEachIndexed { index, data ->
                val encrypted = Des.crypto(data, key, Cipher.ENCRYPT_MODE)
                val decrypted = Des.crypto(encrypted, key, Cipher.DECRYPT_MODE)
                assertArrayEquals(data, decrypted, "边界数据 $index 应该正确处理")
            }
        }
    }
}
